Skip to content

feat(frontend): make quote freshness and currency validation explicit in send flow - #296

Open
woahwhattheheck wants to merge 16 commits into
RemitFlow:mainfrom
woahwhattheheck:goat/remitflow-279-quote-freshness
Open

woahwhattheheck wants to merge 16 commits into
RemitFlow:mainfrom
woahwhattheheck:goat/remitflow-279-quote-freshness

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 24, 2026 •

Copy link
Copy Markdown

Closes #279

Summary

Bind each transfer to the quote the sender reviewed, and refuse confirmation when the quote, wallet connection, connectivity, or available balance cannot support it. This includes the original quote-freshness work and the subsequent locale, balance, and pending-connection corrections.

Changes

  • quoteBinding validates currency pairs, fingerprints priced inputs, checks signability, and reconciles canonical amounts. buildQuote adds a deterministic quote ID, fx.table source, and input fingerprint.
  • QuoteCard displays quote source, ID, timestamps, expiry, and currency precision, with an explicit refresh action.
  • SendMoney invalidates confirmation on priced-input and network changes, blocks expired quotes, and saves the reviewed quoteId in the transfer payload.
  • French keyboard review parses localized input before canonical comparison. Portuguese repeated blur preserves the decimal separator. Canonical balance values are compared without parsing them through the input locale again.
  • Confirmation requires the actual account returned by connect() and checks the bound amount against that account's balance. Rejected/incomplete connections and newly connected insufficient balances stop before transfer creation; affordable and exact-balance transfers remain supported.
  • A confirmation version changes when its quote is invalidated or refreshed. After awaiting wallet connection, a superseded confirmation stops before transfer creation, including when connectivity drops and recovers during the wait. A live connectivity check also blocks current offline submission. The submission lock and finally release remain.
  • Correct null-price feedback for an otherwise valid amount, update the four maintained test files to exercise the actual confirmation/result flow and canonical decimal contract, and apply the existing formatter to its fifteen flagged files. Exact money, offline blocking, submission lock, retry, receipt reconciliation, and no-create assertions remain.

Compatibility and tradeoffs

Quote id/source and Transfer quoteId are additive; legacy records still parse. Identical priced inputs and clock produce the same quote ID; a later refresh produces a new one. Before dispatch, a network interruption clears the pending quote and requires a new review. Already-dispatched transfers retain their existing result handling.

Current validation

Tested source: 17dd96c4. The evidence-only child 91347ff3 and subsequent report update 5b36c889 preserve all tested source files.

  • Complete package selection: 485 passed, zero failed or skipped, across 49 files, 54.94 seconds. This single combined execution includes the earlier quote, locale, balance, and connection cases, with no retries, exclusions, or snapshot changes.
  • Existing Prettier check: passed, with configuration and ignores unchanged.
  • npm audit --audit-level=critical: exit 0; zero critical findings. Remaining findings: 20 high, 12 moderate, and 2 low.
  • Production build: passed; Vite reported 1.46 seconds.
  • All 348 tracked blobs and modes matched the published source before execution and were unchanged afterward.
  • Raw results, source manifest, exact command/configuration, and audit/build/formatter output: report and evidence archive.

Validation boundary

Execution uses the repository's demonstration wallet and transfer service in jsdom. No live wallet/provider/settlement integration or application-performance improvement is established by these results. Dependency files are unchanged; the audit findings above remain. Raw output retains existing React/Vite warnings.

Earlier Chromium and local Lighthouse evidence remains tied to its original tested source and was not rerun in this continuation. The existing Vite development CSP/React-preamble issue is separate.

At the tested source, hosted CI and Lighthouse CI report action_required. Local formatting, tests, critical-audit, and build checks pass; hosted acceptance remains separate.

Show quote source, timestamps, expiry, and currency metadata on the card.
Invalidate pending quotes on field or network changes, refuse to confirm
expired or mismatched prices, and bind the quote id into the transfer
payload so displayed and serialized amounts stay reconciled.

Closes RemitFlow#279
Avoid applying the input locale again after parsing the amount. Keep connected-wallet balance comparisons exact for integer, fractional and canonical string balances, with real App regressions and production before/after screenshots.
Use the repository Prettier configuration for the newly added test block. The normalized Babel AST is identical; production code and observed behavior are unchanged.
Stop when connection returns no account, and compare the bound quote against the returned wallet balance before addTransfer. Cover rejected, insufficient, affordable and exact-balance connections through the actual App and local demonstration service, with production browser before/after screenshots.
…ction

A wallet connection can finish after a network drop has cleared the reviewed quote. Keep a confirmation version across renders and refuse the stale continuation before transfer creation, including when connectivity recovers before the wallet resolves. Field invalidation and quote refresh also retire the earlier confirmation. An explicit new review remains available after the existing submission lock is released.

Two mounted-App regressions reproduce unwanted createTransfer calls on parent 14c8de4 and pass with this change. They use deferred wallet connection with the real local transfer service and storage, assert zero writes for the invalidated attempt, then exactly one transfer bound to a fresh quote after explicit review.

Validation: 75/75 across format, quote-binding, quote-contract, and send-money-quote-freshness suites (Node 24.19.0 / Vitest 4.1.10); production Vite build passed. Package and lock files unchanged. This is a focused continuation; the full suite and hosted CI were not rerun. Earlier documented full-suite failures remain outside this change. Existing contribution and PR retained.
…ecks

A valid amount that cannot be priced now receives a pricing failure message instead of an incorrect amount-validation error. The existing precision fixture traverses review, confirmation and the result dialog before checking submission and receipts. Negative values remain negative in the localized text input and cannot create a transfer.

Preserve exact exponent amounts, serialized fees/rates/receipts, quote-to-receipt equality, contract rejection diagnostics and the existing unquotable/no-create assertion. No production path other than the null-quote message changes.

Focused validation on Node 24.19.0 / Vitest 4.1.10: all 7 cases in test/integration/send-money-precision.test.jsx passed in 8.31s; no skips. Used the retained matching package/lock/runtime, repository Vite config with only cacheDir relocated in an isolated source copy, one worker and disabled test cache. Full suite/build/browser/provider execution was not repeated.

Reproduce: node node_modules/vitest/vitest.mjs run test/integration/send-money-precision.test.jsx --maxWorkers=1 --fileParallelism=false

Executed source blob: 08679cc
Executed test blob: 136b66c
Parent: 6fe01b6
Refs RemitFlow#279
The two existing failing offline/reconnect cases requested review but expected dispatch or navigation immediately. Exercise the actual confirmation and success dialogs before checking a recovered transfer, and explicitly confirm before injecting the existing mid-flight connection failure.

Preserve offline button/handler blocking, the recovery notice, no automatic resubmission, exact once-only creation and the honest unknown-status message. Assert no transfer before explicit confirmation. Pin only the unrelated demonstration wallet's random refusal so the connectivity checks remain deterministic. Product source is unchanged.

One maintained-file execution: all 6 cases in test/integration/offline-reconnect.test.jsx passed, zero skips, 4.75s on Node 24.19.0 / Vitest 4.1.10. Used an isolated source copy, retained matching dependencies, the existing Vite config with only cacheDir redirected, one worker and disabled test cache. Full suite/build/provider execution remains separate.

Reproduce: node node_modules/vitest/vitest.mjs run test/integration/offline-reconnect.test.jsx --maxWorkers=1 --fileParallelism=false

Executed test blob: 363db74
Unchanged SendMoney blob: 08679cc
Parent: 84510d0
Refs RemitFlow#279
Both form and dialog controls display Sending during wallet connection. Scope the disabled-button assertion to the confirmation dialog so it selects the intended control and the test can await completion instead of abandoning pending wallet work.

Pin only the demonstration wallet's random rejection. Preserve validation feedback, no transfer before connection, exactly one transfer after rapid confirm clicks or synchronous submit events, safe-failure retry, and a valid submission after invalid input. Product source is unchanged.

One affected maintained-file execution: test/integration/send-money-form.test.jsx passed all 7 cases, zero skips, in 11.52s on Node 24.19.0 / Vitest 4.1.10. Executed in an isolated source copy with the existing Vite config; only cacheDir was relocated. One worker, no test cache. Full-suite validation remains a separate combined check.

Reproduce: node node_modules/vitest/vitest.mjs run test/integration/send-money-form.test.jsx --maxWorkers=1 --fileParallelism=false --cache=false

Executed test blob: a642327
Unchanged SendMoney blob: 08679cc
Parent: eb89ddb
Refs RemitFlow#279
Quote parsing removes redundant decimal zeros. Preserve exact string and value assertions while expecting canonical 100.1, 0.6, and 99.5 amounts.

Retain the original 0.30 USD floating-point regression. The current minimum fee is 0.25, leaving 0.05 USD; the 17.1 MXN rate gives 0.855, which must round half-up to 0.86. Assert the minimum fee explicitly and retain the below-fee zero clamp using a 0.20 USD quote. No product behavior or parser validation changes, and neither original test is removed or skipped.

Affected maintained file test/unit/quote.test.js: 2 passed, 0 failed, 0 skipped in 1.28s on Node 24.19.0 / Vitest 4.1.10. Executed in an isolated source copy with the existing Vite config, relocating only cacheDir. One worker, no test cache. Combined full-suite validation is separate.

Reproduce: node node_modules/vitest/vitest.mjs run test/unit/quote.test.js --maxWorkers=1 --fileParallelism=false --cache=false

Executed test blob: 9461138
Parent: 9f4f56f
Refs RemitFlow#279
The existing CI runs npm run format:check before tests and build. Its equivalent Prettier check reproduced 15 flagged files on the complete 348-blob tracked source at 4f74e1f. Apply only the existing formatter's output to those exact files; 333 other tracked blobs remain unchanged.

No formatter configuration, ignore rules, dependencies, package scripts, or functional changes. Validation and cache artifacts were kept outside the tracked source copy. Reused installed Prettier 3.9.6 and the repository's existing configuration; no install was performed.

Actual check: node /dev/shm/meridian-da83-rf296-runtime/node_modules/prettier/bin/prettier.cjs --check .
Before: exit 1, 15 files flagged.
Repair: the same CLI with --write and only the 15 flagged paths.
After: exit 0, All matched files use Prettier code style.

No tests rerun for formatting; the single combined package suite and build follow on this composed source.

Formatted paths:
- src/components/QuoteCard.jsx
- src/context/AppContext.jsx
- src/hooks/useTransfers.js
- src/pages/SendMoney.jsx
- src/services/errors.js
- src/services/quote.js
- src/utils/quoteBinding.js
- test/components/WalletButton.test.jsx
- test/integration/offline-reconnect.test.jsx
- test/integration/send-money-form.test.jsx
- test/integration/send-money-quote-freshness.test.jsx
- test/unit/AppContext.wallet.test.jsx
- test/unit/quote-binding.test.js
- test/unit/useTransfers.errors.test.jsx
- vite-plugin-security-headers.js

Parent: 4f74e1f
Refs RemitFlow#279
Record the complete test selection at 17dd96c: 485 passed, zero failed or skipped, across all 49 files. Preserve the 348-file source manifest, unchanged raw Vitest output, formatter evidence, critical-threshold audit and production build receipts.

Record hosted CI and Lighthouse action_required separately from local results, and retain all noncritical audit findings. This commit adds three evidence files and leaves every tested source blob unchanged.

Attribution: GPT-6 Astra Pro / Astra Relay-17 / ChatGPT cloud harness 15a9c3b91fc7.
Preserve all recorded commands, source and runtime identities, validation results, limitations, and raw evidence. Application source and archived execution data are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(frontend): make quote freshness and currency validation explicit in send flow

1 participant